﻿using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE80;
using EnvDTE;
using System.ComponentModel;

namespace dFactor2.CodeHelpers
{
    public class Classes : CodeHelperBase
    {
        public Classes(DTE2 applicationObject) : base(applicationObject) { }

        public void ConvertToSingleton()
        {
            CodeClass codeClass = GetCurrentCodeElement<CodeClass>(vsCMElement.vsCMElementClass);
            if (codeClass == null)
            {
                return;
            }

            //Set all constructors to private
            foreach (CodeElement childElement in codeClass.Children)
            {
                if (childElement.Kind != vsCMElement.vsCMElementFunction)
                {
                    continue;
                }

                CodeFunction codeFunc = childElement as CodeFunction;

                if (codeFunc.FunctionKind != vsCMFunction.vsCMFunctionConstructor)
                {
                    continue;
                }

                codeFunc.Access = vsCMAccess.vsCMAccessPrivate;
            }

            CodeVariable cvInstance = codeClass.AddVariable("_instance", codeClass.Name, 0, vsCMAccess.vsCMAccessPrivate, null);
            cvInstance.IsShared = true;

            CodeProperty cpInstance = codeClass.AddProperty("Instance", "Instance", codeClass.Name, 0, vsCMAccess.vsCMAccessPublic, null);
            cpInstance.Getter.IsShared = true;
            cpInstance.Setter.IsShared = true;

            EditPoint edtPnt = cpInstance.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint();

            edtPnt.Delete(cpInstance.Getter.EndPoint.CreateEditPoint());
            edtPnt.Delete(cpInstance.Setter.EndPoint.CreateEditPoint());

            edtPnt.Insert("get { if(_instance == null) { _instance = new " + codeClass.Name + "(); }\nreturn _instance; }\n private set { _instance = value; }");

            edtPnt.SmartFormat(codeClass.StartPoint);
            edtPnt.SmartFormat(codeClass.EndPoint);
        }

        public void ImplementINotifyPropertyChangedPattern()
        {
            CodeClass codeClass = GetCurrentCodeElement<CodeClass>(vsCMElement.vsCMElementClass);
            if (codeClass == null)
            {
                return;
            }

            foreach (CodeElement implInterfaces in codeClass.ImplementedInterfaces)
            {
                if (implInterfaces.Name.Equals("INotifyPropertyChanged"))
                {
                    return;
                }
            }

            //Add interface
            codeClass.AddImplementedInterface(typeof(INotifyPropertyChanged).FullName, Type.Missing);

            //Add event
            ((CodeClass2)codeClass).AddEvent("PropertyChanged", "PropertyChangedEventHandler", false, -1, vsCMAccess.vsCMAccessPublic);

            //Add method
            CodeFunction codeFunc = codeClass.AddFunction("OnPropertyChanged", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessProtected, null);
            codeFunc.AddParameter("name", vsCMTypeRef.vsCMTypeRefString, 0);

            StringBuilder cfCode = new StringBuilder();
            cfCode.AppendLine("if(string.IsNullOrEmpty(name)) { throw new ArgumentNullException(\"name\"); }\n");
            cfCode.AppendLine("if(PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));  }");

            AddCodeToFunction(codeFunc, cfCode.ToString());
        }

        public void ImplementIDisposablePattern()
        {
            CodeClass codeClass = GetCurrentCodeElement<CodeClass>(vsCMElement.vsCMElementClass);
            if (codeClass == null)
            {
                return;
            }

            bool isDisposable = false;
            List<string> disposableMembers = new List<string>();

            foreach (CodeElement implInterfaces in codeClass.ImplementedInterfaces)
            {
                if (implInterfaces.Name.Equals("IDisposable"))
                {
                    isDisposable = true;
                    break;
                }
            }

            foreach (CodeElement childElement in codeClass.Children)
            {
                if (childElement.Kind != vsCMElement.vsCMElementVariable)
                {
                    continue;
                }

                CodeVariable childVariable = childElement as CodeVariable;

                if (childVariable.Type.TypeKind != vsCMTypeRef.vsCMTypeRefCodeType)
                {
                    continue;
                }

                Type t = Type.GetType(childVariable.Type.CodeType.FullName);

                foreach (Type implInterfaces in t.GetInterfaces())
                {
                    if (implInterfaces.Name.Equals("IDisposable"))
                    {
                        disposableMembers.Add(childVariable.Name);
                        break;
                    }
                }

            }

            if (!isDisposable)
            {
                codeClass.ProjectItem.Open(Constants.vsViewKindCode);

                codeClass.AddImplementedInterface(typeof(IDisposable).FullName, Type.Missing);

                //Declare methods
                CodeFunction cfFinalizer = codeClass.AddFunction(codeClass.ProjectItem.Name, vsCMFunction.vsCMFunctionDestructor, vsCMTypeRef.vsCMTypeRefOther, -1, vsCMAccess.vsCMAccessDefault, null);
                CodeFunction cfDisposeIntrfce = codeClass.AddFunction("Dispose", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessPublic, null);
                CodeFunction cfDisposePattrn = codeClass.AddFunction("Dispose", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessProtected, null);
                cfDisposePattrn.AddParameter("disposing", vsCMTypeRef.vsCMTypeRefBool, 0);

                //Add code
                StringBuilder disposingPattrnCode = new StringBuilder();
                disposingPattrnCode.AppendLine("if(disposing){ GC.SuppressFinalize(this); }");

                foreach (string n in disposableMembers)
                {
                    disposingPattrnCode.AppendLine("if(" + n + " != null) { " + n + ".Dispose(); }");
                }

                AddCodeToFunction(cfFinalizer, "Dispose(false);");
                AddCodeToFunction(cfDisposeIntrfce, "Dispose(true);");
                AddCodeToFunction(cfDisposePattrn, disposingPattrnCode.ToString());
            }

        }

        public void ConstructorBuilder()
        {
            CodeClass codeClass = GetCurrentCodeElement<CodeClass>(vsCMElement.vsCMElementClass);
            if (codeClass == null)
            {
                return;
            }

            Dictionary<string, string> properties = new Dictionary<string, string>();

            foreach (CodeElement childElement in codeClass.Children)
            {
                if (childElement.Kind != vsCMElement.vsCMElementProperty)
                {
                    continue;
                }

                CodeProperty cp = childElement as CodeProperty;

                properties.Add(cp.Name, cp.Type.AsFullName);
            }

            Dialogs.ConstructorBuilder cb = new Dialogs.ConstructorBuilder(properties);
            var res = cb.ShowDialog();
            var selProperties = cb.SelectedProperties;

            cb.Close();

            if (res == System.Windows.Forms.DialogResult.Cancel) { return; }

            CodeFunction ctor = codeClass.AddFunction(codeClass.Name, vsCMFunction.vsCMFunctionConstructor, vsCMTypeRef.vsCMTypeRefVoid, Type.Missing, vsCMAccess.vsCMAccessPublic, Type.Missing);
            EditPoint ep = ctor.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint();

            foreach (string k in selProperties.Keys)
            {
                ctor.AddParameter(k, selProperties[k], Type.Missing);
                ep.Insert(string.Format("this.{0} = {0};\n", k));
            }

            ep.SmartFormat(ctor.StartPoint);
            ep.SmartFormat(ctor.EndPoint);
        }
    }
}
